

#include "unity.h"
#include "unity_fixture.h"

#include "Debug.h"
#include "BSP_Sleep.h"
#include "log/log2.h"

#include "FreeRTOS.h"
#include "task.h"

#include <phNfcLib_Initialization.h>
#include "Utility.h"
#include <Reader/Reader.h>
#include <Reader/ReaderDesfire2.h>
#include <Reader/reader_wavelynx.h>
#include <Reader/reader_samav3.h>
#include "phDriver.h"

#if 0
static uint8_t keys_table[2][16] = {
	{0xD4,0x28,0x31,0xB1,0x16,0x43,0x68,0x44,0xD5,0xAE,0xD5,0x67,0xB1,0xDA,0x14,0xE2},
	{0xd4,0x28,0x31,0xb1,0x16,0x43,0x68,0x44,0xd5,0xae,0xd5,0x67,0xb1,0xda,0x14,0xe2}
};

//	static const uint8_t iso_name1[16] = 
//		{0xA0,0x00,0x00,0x03,0x96,0x56,0x43,0x41,0x03,0xF5,0x32,0xF0,0x00,0x00,0x00,0x00};

static const uint8_t iso_name[16] = {0xa0, 0x00, 0x00, 0x03, 0x96, 0x56, 
				0x43, 0x41, 0x03, 0xf5, 0x32, 0xf0, 0x00, 0x02, 0x09, 0x01};

#pragma pack(1)
	typedef struct {
		uint8_t tag1;
		uint8_t length1;
		uint8_t tag2;
		uint8_t length2;
		uint8_t info;
		uint8_t pdcap[2];
		uint8_t vcuid[7];
		uint8_t version_id;
		uint8_t timestamp[5];
	}fci_info_t;

	typedef struct {
		uint8_t major;
		uint8_t minor;
		uint8_t customer_code[5];
		uint8_t credential_id[8];
		uint8_t reissue_code;
		uint8_t pin_code[4];
		uint8_t customer_data[20];
		uint8_t signature[8];
	}acd_ev1_t;

	typedef struct {
		uint16_t head;
		uint8_t data[8];
		uint8_t wg_num;
		uint8_t wg_format;
	}default_data_t;
	
	typedef struct {
		uint8_t wg_num;
		uint8_t wg_format;
		uint8_t data[18];
	}custom_data_t;
#pragma pack()

static void reader_get_access_data(uint8_t *customer_data, SimpleTag_T* tag){
	default_data_t* def_data = (default_data_t*)customer_data;
	if(def_data->head == 0x1001){
		//默认格式
		uint8_t len = def_data->wg_num / 8;
		if(def_data->wg_num & 0x07){
			len++;
		}
		tag->ubit = def_data->wg_num;
		tag->ulen = len;
		Mem_Backward_Copy(tag->uid, len, def_data->data, sizeof(def_data->data), len);
	}
	else{
		//自定义格式
		custom_data_t* custom = (custom_data_t*)customer_data;
		uint8_t len = custom->wg_num / 8;
		if(custom->wg_num & 0x07){
			len++;
		}
		tag->ubit = custom->wg_num;
		tag->ulen = len;
		Mem_Backward_Copy(tag->uid, len, custom->data, sizeof(custom->data), len);
	}
}

static void reader_print_fci(const fci_info_t* fci){
	LOG2_PRINT("\n===========FCI============\n");
	LOG2_PRINT("info: %02X\n", fci->info);
	LOG2_PRINT("pdcap: %02X%02X\n", fci->pdcap[0], fci->pdcap[1]);
	LOG2_PRINT("vcuid: %02X%02X%02X%02X%02X%02X%02X\n", fci->vcuid[0], fci->vcuid[1], 
		fci->vcuid[2], fci->vcuid[3], fci->vcuid[4], fci->vcuid[5], fci->vcuid[6]);
	LOG2_PRINT("version_id: %02X\n", fci->version_id);
	LOG2_PRINT("timestamp: %02X%02X%02X%02X%02X\n", fci->timestamp[0], fci->timestamp[1], 
		fci->timestamp[2], fci->timestamp[3], fci->timestamp[4]);
	LOG2_PRINT("===========================\n");
}

static uint8_t reader_mf2go_gen_div(const fci_info_t* fci, const uint8_t aid[3], uint8_t *div){
	uint8_t len = 0;
//		div[len++] = 0x01;
	memcpy(&div[len], fci->vcuid, sizeof(fci->vcuid));
	len += sizeof(fci->vcuid);
	memcpy(&div[len], aid, 3);
	len += 3;
	div[len++] = fci->info;
	memcpy(&div[len], fci->timestamp, sizeof(fci->timestamp));
	len += sizeof(fci->timestamp);
	return len;
}

static uint32_t reader_read_mf2go(SimpleTag_T* info){
	int ret;
	phStatus_t status;
	void* pDataParams  = phNfcLib_GetDataParams(PH_COMP_AL_MFDFEVX);
	uint8_t keyNo = 0;
	uint8_t key_no = 0;
	uint8_t ver = 0;
	void* key_store = phNfcLib_GetDataParams(PH_COMP_KEYSTORE);
	
	fci_info_t fci_info = {0};
#if 1
{
	uint8_t df_name[16] = {0xa0, 0x00, 0x00, 0x03, 0x96, 0x56, 0x43, 0x41, 
		0x03, 0xf5, 0x32, 0xf0, 0x00, 0x02, 0x09, 0x01};
	uint8_t *pFCI = NULL;
	uint16_t wFCILen = 0;
	status = phalMfdfEVx_IsoSelectFile(pDataParams, 0x00U, 0x04, NULL, 
		(uint8_t*)iso_name, sizeof(iso_name), 0, &pFCI, &wFCILen);
	if(status){
		LOG2_ERR("phalMfdfEVx_IsoSelectFile fail: %04X", status);
		return status;
	}
	LOG2_BUF_DBG("FCI", pFCI, wFCILen);
	
	uint8_t len = wFCILen < sizeof(fci_info) ? wFCILen : sizeof(fci_info);
	memcpy(&fci_info, pFCI, len);
	reader_print_fci(&fci_info);
}
#endif 
//1. Activate and identify credential using ISO SELECT.
#if 0
{
//			uint8_t capdu[] = {0x00, 0xa4, 0x04, 0x00, 0x10, 0xa0, 0x00, 0x00, 0x03, 0x96, 0x56, 
//				0x43, 0x41, 0x03, 0xf5, 0x32, 0xf0, 0x00, 0x02, 0x09, 0x01, 0x00};
		uint8_t clen = 0;
		uint8_t capdu[32] = {0x00, 0xa4, 0x04, 0x00, 0x10,};
		clen = 5;
		memcpy(capdu + clen, iso_name, sizeof(iso_name));
		clen += sizeof(iso_name);
		capdu[clen++] = 0;
		uint8_t *rapdu;
		uint16_t rlen;
		LOG2_DBG("select app");
		status = phpalI14443p4_Exchange(&gphNfcLib_Params.spalI14443p4, PH_EXCHANGE_DEFAULT, capdu, clen, &rapdu, &rlen);
		if(status){
			return status;
		}
		if(rlen < 2){
			LOG2_BUF_ERR("resp", rapdu, rlen);
			return 5;
		}
		uint8_t sw[2];
		sw[0] = rapdu[rlen-2];
		sw[1] = rapdu[rlen-1];
		if(memcmp(sw, (uint8_t[]){0x90, 0x00}, 2)){
			LOG2_ERR("sw: %02X%02X", sw[0], sw[1]);
			return 6;
		}
		LOG2_BUF_DBG("FCI", rapdu, rlen);
//			LOG_B("FCI:", rapdu, rlen, 16);
		uint8_t len = rlen < sizeof(fci_info) ? rlen : sizeof(fci_info);
		memcpy(&fci_info, rapdu, len);
		reader_print_fci(&fci_info);
}
#endif 

#if 0
{
		uint8_t capdu[] = {0x00, 0xa4, 0x04, 0x00, 0x10, 0xa0, 0x00, 0x00, 0x03, 0x96, 0x56, 
			0x43, 0x41, 0x03, 0xf5, 0x32, 0xf0, 0x00, 0x02, 0x09, 0x01, 0x00};
		uint8_t *rapdu;
		uint16_t rlen;
		status = phpalI14443p4_Exchange(&gphNfcLib_Params.spalI14443p4, PH_EXCHANGE_DEFAULT, capdu, sizeof(capdu), &rapdu, &rlen);
		if(status){
			return status;
		}
		LOG_B("FCI:", rapdu, rlen, 16);
		uint8_t len = rlen < sizeof(fci_info) ? rlen : sizeof(fci_info);
		memcpy(&fci_info, rapdu, len);
		reader_print_fci(&fci_info);
}
#endif 
//		2. DESFire application 0xF532F0 is auto selected as primary application.
#if 0
	uint32_t aid = 0xF532F0;
	ret = Reader_MFDF_SelectApplication(PHAL_MFDFEVX_SELECT_PRIMARY_APP, (uint8_t*)&aid, NULL);
	if(ret){
		LOG2_ERR("select app fail: %d", ret);
		return ret;
	}
#endif 
//		3. Read/Write encryption key for application is diversified using FCI data.
	uint8_t aid[3] = {0xF5,0x32,0xF0};
	uint8_t k0[16];
	uint8_t div[32] = {0};
	uint8_t dlen = reader_mf2go_gen_div(&fci_info, aid, div);
	
	phalMfdfEVx_SetConfig(pDataParams, PHAL_MFDFEVX_WRAPPED_MODE, 1);
	
	LOG2_BUF_DBG("div", div, dlen);
//		4. DESFire application is authenticated using key 1.
{
		keyNo = 1;
		key_no = 1;
		uint8_t* key_aes = &keys_table[1][0];
		LOG2_BUF_DBG("key", key_aes, 16);
		phKeyStore_FormatKeyEntry(key_store, keyNo, PH_KEYSTORE_KEY_TYPE_AES128);
		phKeyStore_SetKeyAtPos(key_store, keyNo, 0,
								 PH_KEYSTORE_KEY_TYPE_AES128, (uint8_t *)key_aes, ver);
		ret = Reader_MFDF_AuthenticateAES(PHAL_MFDFEVX_DIV_METHOD_CMAC, keyNo, ver, key_no, div, dlen);
		if(ret){
			LOG2_ERR("Reader_MFDF_Authenticate fail: %04X", ret);
			return ret;
		}
}

//		5. Credential data is read from file 2 of application.
//	acd_ev1_t acd = {0};
uint8_t bFileNo = 2;

{
	uint8_t *data;
	uint16_t dlen = 0;
	uint8_t offset[3] = {0x00, 0x00, 0x00};
	uint8_t length[3] = {sizeof(acd_ev1_t), 0x00, 0x00};
	LOG2_DBG("read data");
	ret = Reader_MFDF_ReadData(PHAL_MFDFEVX_COMMUNICATION_ENC, PHAL_MFDFEVX_APPLICATION_CHAINING, bFileNo, offset, length, &data, &dlen);
	if(ret){
		LOG2_ERR("Reader_MFDF_ReadData fail: %04X", ret);
		return 6;
	}
	LOG2_BUF_DBG("data", data, dlen);
	if(dlen != sizeof(acd_ev1_t)){
		LOG2_ERR("dlen=%d, len=%d", dlen, sizeof(acd_ev1_t));
		return 7;
	}
	SimpleTag_T tag;
	acd_ev1_t* acd = (acd_ev1_t*)data;
	reader_get_access_data(acd->customer_data, &tag);
	LOG2_DBG("wg: %d", tag.ubit);
	LOG2_BUF_DBG("data", tag.uid, tag.ulen);
}
//		6. A WRITE command is performed on the Reissue Code field.
#if 1
{
	uint8_t data[1] = {0};
	uint16_t dlen = 0;
	uint8_t offset[3] = {0x01, 0x00, 0x00};
	uint8_t length[3] = {1, 0x00, 0x00};
	LOG2_DBG("write data");
	ret = Reader_MFDF_WriteData(PHAL_MFDFEVX_COMMUNICATION_ENC, PHAL_MFDFEVX_APPLICATION_CHAINING, bFileNo, offset, data, length);
	if(ret){
		LOG2_ERR("Reader_MFDF_ReadData fail: %04X", ret);
		return 6;
	}
}
#endif 
//		7. A COMMIT TRANSACTION command is performed to end exchange.

#if 1
	uint8_t end_apdu[] = {0x90, 0xC7, 0x00, 0x00, 0x00};
	uint8_t *end_rapdu;
	uint16_t end_rlen;
	LOG2_DBG("COMMIT TRANSACTION");
	phpalI14443p4_Exchange(&gphNfcLib_Params.spalI14443p4, PH_EXCHANGE_DEFAULT, end_apdu, sizeof(end_apdu), &end_rapdu, &end_rlen);
//7. DESELECT credential.
	phpalI14443p4_Deselect(&gphNfcLib_Params.spalI14443p4);
#endif 

	return 0;
}
#endif 

TEST_GROUP(Desfire2);


TEST_SETUP(Desfire2)
{
	reader.hfFlags = TAG_MASK_MIFARE;
	Reader_NFC_LoadProfile(reader.hfFlags, 0, 0, 0);
}

TEST_TEAR_DOWN(Desfire2)
{
	Reader_NFC_RfOff();
}


#if 0
TEST(Desfire2, read_wavelynx)
{
	int ret;
	int i;
	credential_t cred;

	reader_wavelynx_init();
	reader_wavelynx_load_keys();
	for(i = 0; i < 100000; i++){
		Reader_NFC_RfOff();
		BSP_Sleep_Ms(500);
		ret = Reader_NFC_DiscoveryStd3(&cred.tag, PHAC_DISCLOOP_POS_BIT_MASK_A, 5);
		if(ret){
			continue;
		}
		ret = reader_read_wavelynx_tag(&reader, &cred);
		if(ret){
			continue;
		}
		LOG2_BUF_INF("uid", cred.credential, cred.clen);
		
#if 0
		ret = reader_read_mf2go(&reader.curHfTag);
		if(ret){
			continue;
		}
#endif 
	}
}
#endif 

static int reader_wavelynx_reset_default_key(void){
	int ret = 0;
	ret = samav3_disable_key_entry(SAM_KID_GOOGLE);
	if(ret){
		LOG2_ERR("samav3_reset_key_entry(%d) fail: %d", SAM_KID_GOOGLE, ret);
		return ret;
	}	
	ret = samav3_disable_key_entry(SAM_KID_APPLE_PRIV);
	if(ret){
		LOG2_ERR("samav3_disable_key_entry(%d) fail: %d", SAM_KID_APPLE_PRIV, ret);
		return ret;
	}
	ret = samav3_disable_key_entry(SAM_KID_APPLE_CRED);
	if(ret){
		LOG2_ERR("samav3_disable_key_entry(%d) fail: %d", SAM_KID_APPLE_CRED, ret);
		return ret;
	}

	return 0;
}

//	int wavelynx_main(void);
int reader_wavelynx_init(void);
int reader_wavelynx_load_keys(void);

// int reader_wavelynx_read(credential_t* cred);
int reader_wavelynx_config_tag(credential_t* cred);
int reader_wavelynx_load_default_keys(void);

int reader_wavelynx_gen_mac(void);

int reader_cfg_load_default_key(void);
void reader_wavelynx_config_init(void);
int reader_wavelynx_config3(SimpleTag_T* tag);

phStatus_t phDriver_TimerStart(phDriver_Timer_Unit_t eTimerUnit, uint32_t dwTimePeriod, pphDriver_TimerCallBck_t pTimerCallBack);

TEST(Desfire2, read_wavelynx)
{
	int ret;
	phStatus_t	status = 0;
#if 0
	samav3_init();
	samav3_activate();
	reader_wavelynx_reset_default_key();
	reader_wavelynx_load_default_keys();
	
	ret = reader_cfg_load_default_key();
	TEST_ASSERT_EQUAL_INT(0, ret);
	reader_wavelynx_config_init();
	
	reader_wavelynx_init();
	samav3_deactivate();
#endif 

	/* Load DESFire default keys into SAM */
	// reader_wavelynx_load_keys();

	// ret = reader_wavelynx_gen_mac();
	/*----------------------------------*/
	/*SAM AV3 is now prepared and active*/
	/*----------------------------------*/
	credential_t cred;
	while(1){
		Reader_NFC_RfOff();
		// LOG2_INF("timer start");
		// phDriver_TimerStart(PH_DRIVER_TIMER_MILLI_SECS, 20, NULL);
		// LOG2_INF("timer end");		
		BSP_Sleep_Ms(500);
		ret = Reader_NFC_DiscoveryStd3(&cred.tag, PHAC_DISCLOOP_POS_BIT_MASK_A, 5);
		if(!ret){
			
			samav3_activate2();
			// ret = reader_wavelynx_read(&cred);
			
			LOG2_INF("wavelynx start!");
			uint32_t t0 = xTaskGetTickCount();
			ret = reader_read_wavelynx_tag(&reader, &cred);
			log_reset_level();
			LOG2_INF("wavelynx time: %u ms", (uint32_t)(xTaskGetTickCount() - t0));
			LOG2_INF("wavelynx end!");
			if(!ret){
					LOG2_BUF_INF("CRED", cred.credential, cred.clen);
					BSP_Sleep_Ms(500);
			}
			else
			if(ret == 1 || ret == 2){
				 ret = reader_wavelynx_config3(&cred.tag);
			}
			
			samav3_deactivate();
			// ret = reader_wavelynx_gen_mac();
		}
	}
}


